#include "ProxyUnit_modbus.h"

#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

// 预定义的默认值数组
static const uint8_t DEFAULT_PROTOCOL_VERSION[] = {0x01, 0x01};

// 档案信息
typedef struct {
    uint32_t reg_addr;                  // 寄存器地址
    uint8_t data_size;                  // 寄存器数据占用字节数
    const uint8_t* default_data;        // 默认值
    uint8_t default_size;               // 默认值大小（若无默认值，则表示档案信息占用字节数）
} DeviceInfoItem;

typedef struct {
    char *type;
    DeviceInfoItem info[INFO_MAX_TYPES];
} DeviceItem;

static const DeviceItem device_info[] = {
    // 产品类型1: xxxx产品
    {"xxxx_product",
    {{100, 1, NULL, 2},
     {300, 64, NULL, 16},
     {200, 2, NULL, 2},
     {400, 64, NULL, 16},
     {219, 32, NULL, 16}}
    },

    // 此处添加其他产品类型
    //{"...",
    //{{..., ..., ..., ...},
    // {..., ..., ..., ...},
    // {..., ..., ..., ...},
    // {..., ..., ..., ...},
    // {..., ..., ..., ...}}
    //},
    ////////////////////

    // 默认产品类型:
    {"",                                    // 产品型号标识（空值表示默认视为该产品）
    {{0, 1, NULL, 2},                       // 通信地址
     {1056, 64, NULL, 16},                  // 设备ID
     {0, 2, DEFAULT_PROTOCOL_VERSION, 2},   // 通信协议版本号
     {1088, 64, NULL, 16},                  // 产品型号
     {219, 32, NULL, 16}}                   // 软硬件版本
    }
};


/**
 * @brief Modbus RTU CRC16计算
 * @param data 待计算CRC的数据指针
 * @param length 数据长度（字节数）
 * @return 计算得到的CRC16值（小端格式）
 * @note 多项式：0xA001，初始值：0xFFFF
 */
uint16_t modbus_rtu_crc16(uint8_t *data, uint16_t length) {
    uint16_t crc = MODBUS_CRC16_INIT_VALUE;
    uint8_t i;

    while (length--) {
        crc ^= *data++;
        for (i = 0; i < 8; i++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ MODBUS_CRC16_POLYNOMIAL;
            } else {
                crc >>= 1;
            }
        }
    }

    return crc;
}


/**
 * @brief 验证Modbus RTU帧的CRC校验
 * @param frame 指向已解析的Modbus帧结构体
 * @return true-CRC校验通过，false-CRC校验失败
 * @note 会自动打印调试信息（需启用DEBUG模式）
 */
bool modbus_rtu_validate_frame(modbus_rtu_frame_t *frame) {
    // 计算接收数据的CRC
    uint8_t buffer[256];
    uint16_t length = 0;

    buffer[length++] = frame->address;
    buffer[length++] = frame->function;
    memcpy(&buffer[length], frame->data, frame->data_length);
    length += frame->data_length;

    uint16_t calculated_crc = modbus_rtu_crc16(buffer, length);

    // 比较计算的CRC和接收的CRC
    return (calculated_crc == frame->crc);
}


/**
 * @brief 解析Modbus RTU帧
 * @param data 原始数据指针
 * @param length 数据总长度（含CRC）
 * @param frame 输出解析后的帧结构体
 * @note 自动处理小端格式的CRC
 */
void modbus_rtu_parse_frame(uint8_t *data, uint16_t length, modbus_rtu_frame_t *frame) {
    if (length < 4) return; // 最小帧长: 地址(1) + 功能码(1) + CRC(2)

    frame->address = data[0];
    frame->function = data[1];
    frame->data_length = length - 4; // 减去地址、功能码和CRC

    if (frame->data_length > 0) {
        memcpy(frame->data, &data[2], frame->data_length);
    }

    // 提取CRC (小端格式)
    frame->crc = (data[length - 1] << 8) | data[length - 2];
}


/**
 * @brief 构建Modbus RTU帧
 * @param address 从站地址
 * @param function 功能码
 * @param data PDU数据指针
 * @param data_length PDU数据长度
 * @param frame 输出缓冲区
 * @return 构建的帧总长度（含CRC）
 * @note 自动添加CRC校验（小端格式）
 */
uint16_t modbus_rtu_build_frame(uint8_t address, uint8_t function, 
                              uint8_t *data, uint16_t data_length, 
                              uint8_t *frame) {
    uint16_t length = 0;

    // 添加地址和功能码
    frame[length++] = address;
    frame[length++] = function;

    // 添加数据
    if (data_length > 0) {
        memcpy(&frame[length], data, data_length);
        length += data_length;
    }

    // 计算并添加CRC (小端格式)
    uint16_t crc = modbus_rtu_crc16(frame, length);
    frame[length++] = crc & 0xFF;     // CRC低字节
    frame[length++] = (crc >> 8) & 0xFF; // CRC高字节
    
    return length;
}


/**
 * @brief Modbus主站读取响应
 * @param response 响应数据缓冲区
 * @param response_length 返回的响应长度
 * @return true-响应有效且CRC校验通过，false-无响应或校验失败
 * @note 会自动解析和验证帧结构
 */
bool modbus_rtu_master_read_response(uint8_t *response, uint16_t *response_length) {
    uint16_t data_length = usart6_get_rx_data_len();
    if (data_length == 0) {
        return false;
    }

    // 读取接收到的数据
    *response_length = usart6_read_rx_data(response, data_length);

    // 解析帧
    modbus_rtu_frame_t frame;
    modbus_rtu_parse_frame(response, *response_length, &frame);

    // 验证帧
    if (!modbus_rtu_validate_frame(&frame)) {
        return false;
    }

    return true;
}


//=====================================================================================================
//==================================================使用的函数==========================================
//=====================================================================================================


/**
 * @brief Modbus主站发送请求
 * @param slave_address 从站地址
 * @param function 功能码
 * @param start_address 起始地址
 * @param quantity 寄存器/线圈数量
 * @param data 写入数据指针（写操作时使用）
 * @param data_length 写入数据长度（字节数）
 * @return true-发送成功，false-功能码不支持
 * @note 自动构建完整帧并通过USART发送
 */
bool modbus_rtu_master_send_request(uint8_t slave_address, uint8_t function, 
                                   uint16_t start_address, uint16_t quantity, 
                                   uint16_t *data, uint16_t data_length) {
    uint8_t frame[256];
    uint8_t pdu[256];
    uint16_t pdu_length = 0;

    // 构建PDU (协议数据单元)
    pdu[pdu_length++] = (start_address >> 8) & 0xFF;  // 起始地址高字节
    pdu[pdu_length++] = start_address & 0xFF;         // 起始地址低字节

    switch (function) {
        case MODBUS_READ_COILS:
        case MODBUS_READ_DISCRETE_INPUTS:
        case MODBUS_READ_HOLDING_REGISTERS:
        case MODBUS_READ_INPUT_REGISTERS:
            pdu[pdu_length++] = (quantity >> 8) & 0xFF; // 数量高字节
            pdu[pdu_length++] = quantity & 0xFF;       // 数量低字节
            break;

        case MODBUS_WRITE_SINGLE_COIL:
        case MODBUS_WRITE_SINGLE_REGISTER:
            pdu[pdu_length++] = (data[0] >> 8) & 0xFF; // 数据高字节
            pdu[pdu_length++] = data[0] & 0xFF;        // 数据低字节
            break;

        case MODBUS_WRITE_MULTIPLE_COILS:
        case MODBUS_WRITE_MULTIPLE_REGISTERS:
            pdu[pdu_length++] = (quantity >> 8) & 0xFF; // 数量高字节
            pdu[pdu_length++] = quantity & 0xFF;         // 数量低字节
            pdu[pdu_length++] = data_length;             // 字节计数

            memcpy(&pdu[pdu_length], (uint8_t*)data, data_length);
            pdu_length += data_length;
            break;

        default:
            return false;
    }

    // 构建完整帧
    uint16_t frame_length = modbus_rtu_build_frame(slave_address, function, pdu, pdu_length, frame);

    // 发送帧
    usart6_send_data_it(frame, frame_length);

    return true;
}


/**
 * @brief 读取并解析Modbus从站响应
 * @param response 存储响应数据的缓冲区
 * @param response_len 返回实际接收的数据长度
 * @param parsed_frame 输出解析后的帧结构体(可选)
 * @return true-响应有效且解析成功，false-响应无效或解析失败
 * @note 不再包含任何打印语句，仅做数据校验和解析
 */
bool modbus_rtu_read_and_parse_response(uint8_t *response, uint16_t *response_len, 
                                      modbus_rtu_frame_t *parsed_frame) {
    // 1. 读取原始响应数据
    if (!modbus_rtu_master_read_response(response, response_len)) {
        return false;
    }

    // 2. 解析帧结构
    modbus_rtu_frame_t frame;
    modbus_rtu_parse_frame(response, *response_len, &frame);

    // 3. 校验CRC
    if (!modbus_rtu_validate_frame(&frame)) {
        return false;
    }

    // 4. 处理异常响应（功能码最高位为1）
    if (frame.function & 0x80) {
        return false;
    }

    // 5. 如果需要返回解析后的帧
    if (parsed_frame != NULL) {
        *parsed_frame = frame;
    }

    return true;
}


/**
 * @brief 写单个保持寄存器
 * @param slave_address 从站地址
 * @param reg_address 寄存器地址
 * @param value 要写入的值（16位）
 * @return true-发送成功，false-发送失败
 */
bool modbus_write_single_register(uint8_t slave_address, uint16_t reg_address, uint16_t value) {
    uint16_t data[1] = {value};
    return modbus_rtu_master_send_request(
        slave_address,
        MODBUS_WRITE_SINGLE_REGISTER,
        reg_address,
        1,          // 固定为1个寄存器
        data,
        0           // 数据长度由内部处理
    );
}


/**
 * @brief 写多个保持寄存器
 * @param slave_address 从站地址
 * @param start_address 起始寄存器地址
 * @param values 要写入的数据数组
 * @param num_registers 寄存器数量（最多124个）
 * @return true-发送成功，false-发送失败
 */
bool modbus_write_multiple_registers(uint8_t slave_address, 
                                    uint16_t start_address, 
                                    uint16_t *values, 
                                    uint16_t num_registers) {
    if (num_registers > 124) return false; // Modbus协议限制
    
    // 转换为字节流（大端序）
    uint8_t byte_data[256];
    for (int i = 0; i < num_registers; i++) {
        byte_data[i*2]   = (values[i] >> 8) & 0xFF; // 高字节
        byte_data[i*2+1] = values[i] & 0xFF;        // 低字节
    }

    return modbus_rtu_master_send_request(
        slave_address,
        MODBUS_WRITE_MULTIPLE_REGISTERS,
        start_address,
        num_registers,
        (uint16_t*)byte_data,
        num_registers * 2 // 数据长度（字节数）
    );
}


/**
 * @brief 写单个线圈（开关量）
 * @param slave_address 从站地址
 * @param coil_address 线圈地址
 * @param state 状态（true=ON, false=OFF）
 * @return true-发送成功，false-发送失败
 */
bool modbus_write_single_coil(uint8_t slave_address, uint16_t coil_address, bool state) {
    uint16_t data[1] = {state ? 0xFF00 : 0x0000}; // Modbus规范：0xFF00表示ON
    return modbus_rtu_master_send_request(
        slave_address,
        MODBUS_WRITE_SINGLE_COIL,
        coil_address,
        1,          // 固定为1个线圈
        data,
        0           // 数据长度由内部处理
    );
}


/**
 * @brief 扫描Modbus设备并读取保持寄存器数据
 * @param start_addr 起始设备地址
 * @param end_addr 结束设备地址
 * @param device_data 设备数据缓冲区
 * @param max_devices 设备数据缓冲区最大容量
 * @param found_devices 返回找到的设备数量
 * @return true-扫描成功，false-扫描失败或参数错误
 * @note 此函数会依次扫描地址范围内的设备，并读取每个设备的保持寄存器数据
 */
 bool modbus_scan_and_read_devices(uint8_t start_addr, uint8_t end_addr, 
                                modbus_device_data_t *device_data, 
                                uint8_t max_devices, 
                                uint8_t *found_devices) {
    // 参数校验
    if (start_addr > end_addr || end_addr > MODBUS_RTU_ADDRESS_MAX || 
        device_data == NULL || max_devices == 0 || found_devices == NULL) {
        printf("Active_Report: para error\n");
        return false;
    }

    uint8_t found = 0;
    uint8_t response[256];
    uint16_t response_len;
    modbus_rtu_frame_t frame;

    // 初始化设备数据结构
    memset(device_data, 0, sizeof(modbus_device_data_t) * max_devices);
    
    // ===== 第一阶段：基础扫描 =====
    for (uint8_t addr = start_addr; addr <= end_addr && found < max_devices; addr++) {
        bool devOnline = false;
        uint8_t productId;         // 产品类型下标

        // 优先尝试匹配具体产品类型
        for (productId = 0; productId < sizeof(device_info) / sizeof(DeviceItem); productId ++) {
            // 发送读取请求
            DeviceItem *reg = &device_info[productId];
            if (!modbus_rtu_master_send_request(addr, MODBUS_READ_HOLDING_REGISTERS, 
                                              reg->info[INFO_PRODUCT_MODEL].reg_addr, 
                                              (reg->info[INFO_PRODUCT_MODEL].default_size+1) / 2, NULL, 0)) {
                continue;
            }

            // 等待响应
            LOS_TaskDelay(MODBUS_RTU_RESPONSE_TIMEOUT);

            if (modbus_rtu_read_and_parse_response(response, &response_len, &frame)) {
                devOnline = true;
                if (frame.function == MODBUS_READ_HOLDING_REGISTERS && 
                    frame.address == addr && 
                    strncmp(&frame.data[1], reg->type, strlen(reg->type)) == 0) {
                    // 存储基础信息
                    device_data[found].address = addr;
                    device_data[found].product_type = productId;

                    // 解析设备地址和产品ID
                    memcpy(device_data[found].registers, &addr, 1);
                    // 转换产品ID为大端序
                    uint8_t regCnt = (reg->info[INFO_COMM_ADDRESS].default_size + reg->info[INFO_DEVICE_ID].default_size 
                                  + reg->info[INFO_PROTOCOL_VERSION].default_size) / 2;
                    for (int idx = 0; idx < (reg->info[INFO_PRODUCT_MODEL].default_size) / 2; idx ++) {
                        device_data[found].registers[regCnt + idx] = ((uint16_t)frame.data[2*idx + 1] << 8) | frame.data[2*idx + 2];
                    }

                    found++;
                    break;
                }
            }
        }
    }

    // ===== 第二阶段：补充档案信息 =====
    for (uint8_t i = 0; i < found; i++) {
        uint8_t addr = device_data[i].address;
        uint8_t productType = device_data[i].product_type;
        uint8_t pos = 0;    // 当前档案信息下标

        // 遍历所有信息类别
        for (int info_type = INFO_COMM_ADDRESS; info_type < INFO_MAX_TYPES; info_type++) {
            DeviceInfoItem *item = &device_info[productType].info[info_type];

            // 跳过已通过基础扫描获取的字段
            if (info_type == INFO_COMM_ADDRESS || info_type == INFO_PRODUCT_MODEL) {
                pos += item->default_size;
                continue;
            }

            // 若有默认值，则使用默认值
            if (item->default_data != NULL && item->default_size > 0)
            {
                uint8_t *target = (uint8_t*)device_data[i].registers + pos;
                memcpy(target, item->default_data, item->default_size);
                pos += item->default_size;
                continue;
            }

            // 发送特定信息的读取请求
            uint16_t regs_to_read = (item->data_size + 1) / 2; // 寄存器数量
            if (!modbus_rtu_master_send_request(addr, MODBUS_READ_HOLDING_REGISTERS, 
                                              item->reg_addr, regs_to_read, NULL, 0)) {
                pos += item->default_size;
                continue;
            }

            LOS_TaskDelay(MODBUS_RTU_RESPONSE_TIMEOUT);
            
            if (modbus_rtu_read_and_parse_response(response, &response_len, &frame)) {
                if (frame.function == MODBUS_READ_HOLDING_REGISTERS) {
                    // 计算实际数据长度（取设备定义长度和返回长度的最小值）
                    uint16_t data_len = MIN(item->default_size, frame.data[0]);
                    
                    // 存储到设备结构（按信息类型偏移）
                    uint16_t *target = (uint8_t*)device_data[i].registers + pos;
                    for (int idx = 0; idx < (data_len + 1) / 2; idx ++) {
                        target[idx] = ((uint16_t)frame.data[2*idx + 1] << 8) | frame.data[2*idx + 2];
                    }
                }
            }
            pos += item->default_size;
        }
        device_data[i].register_count = pos / 2;
    }

    *found_devices = found;

    return true;
}
